/*
 * Version 0.2.1
 *
 * Copyright (c) 2010 Guet
 *
 * All rights reserved.
 *
 * Developed by: XGuru
 *
 */
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>

#include "hash.h"

struct hashtable *hash_init (unsigned int size)
{
    struct hashtable *tablestruct;
    int counter;

    /* Our hash function only works with power-of-2 bucket sizes for speed. */

    tablestruct = malloc (sizeof (struct hashtable));
    if (!tablestruct)
    {
        return NULL;
    }
    tablestruct->table = malloc (size * sizeof (struct hashentry *));
    if (!tablestruct->table)
    {
        return NULL;
    }
    for (counter=0; counter<size; counter++)
    {
        tablestruct->table[counter] = NULL;
    }
    tablestruct->size = size;
    tablestruct->spares = NULL;

    return tablestruct;
}

// JS Hash Function
unsigned int hash_hash(char *key)
{

    unsigned int hash = 1315423911;
    while (*key)
    {
        hash ^= ((hash << 5) + (*key++) + (hash >> 2));
    }
    /*printf ("new key is %d\n ",(hash & 0x7FFFFFFF));*/
    return (hash & 0x7FFFFFFF);
}

struct hashentry *hash_lookup (struct hashtable *tablestruct, char *key)
{
    unsigned int hash;
    struct hashentry *entry;

    hash = hash_hash (key) & (tablestruct->size - 1);
    /*printf("array index :%d\n",hash);*/
    for (entry = tablestruct->table[hash]; entry != NULL; entry = entry->next)
    {
        if (strcmp(entry->key,key) == 0)
        {
            /*printf("Get the Value is %s\n",entry->value);*/
            return entry;
        }
    }

    return NULL;
}

/* Add the key to the hash table. Assumes the key is not already present. */
int hash_insert (struct hashtable *tablestruct,char *key,int flag,unsigned int exptime,int length, char *value)
{
    unsigned int hash;
    struct hashentry *entry;

    hash = hash_hash (key) & (tablestruct->size - 1);
    /*printf ("bucket is %d\n", hash);*/

    entry = malloc (sizeof (struct hashentry));
    if (!entry)
    {
        return -1;
    }
    entry->key = key;
    entry->value = value;
    entry->flag = flag;
    entry->exptime = exptime;
    entry->length = length;
    time(&entry->init_time);
    /*printf("key: %s\n",key);
    printf("value: %s\n",value);
    printf("flag: %d\n",flag);
    printf("exptime: %d\n",exptime);
    printf("init_time:%ld\n",entry->init_time);
    printf("length: %d\n",length);
    */
    entry->next = tablestruct->table[hash];

    tablestruct->table[hash] = entry;
    return 0;
}

/* Removes the key from the hash table. Does not signal an error if the key
 * was not present. */
int hash_remove (struct hashtable *tablestruct, char *key)
{
    unsigned int hash;
    struct hashentry *entry, *tmpentry;

    hash = hash_hash (key) & (tablestruct->size - 1);
    entry = tablestruct->table[hash];
    /* If this is the first entry then it needs special handling. */
    if (entry && (strcmp(entry->key,key) == 0))
    {
        tmpentry = entry->next;
        free (entry);
        tablestruct->table[hash] = tmpentry;
        return 0;
    }
    else
    {
        while (entry)
        {
            if (entry->next && (strcmp(entry->next->key,key) == 0) )
            {
                tmpentry = entry->next;
                entry->next = entry->next->next;
                free (tmpentry);
                return 0;
            }
            entry = entry->next;
        }
    }
    return -1;
}

void hash_free (struct hashtable *tablestruct)
{
    int counter;
    struct hashentry *entry, *prev;

    /* Need to free buckets and table struct and every item in every chain */
    for (counter=0; counter<tablestruct->size; counter++)
    {
        entry = tablestruct->table[counter];
        while (entry)
        {
            prev = entry;
            entry = entry->next;
            free (prev);
        }
    }
    free (tablestruct->table);
    free (tablestruct);
}
